//
//  M80AttributedLabel.m
//  M80AttributedLabel
//
//  Created by amao on 13-9-1.
//  Copyright (c) 2013年 www.xiangwangfeng.com. All rights reserved.
//

#import "M80AttributedLabel.h"
#import "M80AttributedLabelAttachment.h"
//#import "M80AttributedLabelURL.h"
#import "M80AttributedLabelCustomLink.h"
#import "RegexKitLite.h"

#if !__has_feature(objc_arc)

#error 这个"M80AttributedLabel" 类需要自动引用计数

#endif


/* 省略号**/
static NSString* const kEllipsesCharacter = @"\u2026";

//static dispatch_queue_t m80_attributed_label_parse_queue;
//static dispatch_queue_t get_m80_attributed_label_parse_queue() {
//
//    if (m80_attributed_label_parse_queue == NULL) {
//        m80_attributed_label_parse_queue = dispatch_queue_create("com.m80.parse_queue", 0);
//    }
//    return m80_attributed_label_parse_queue;
//}

@interface M80AttributedLabel () {
    
    NSMutableArray              *_attachments;
    NSMutableArray              *_linkLocations;
    CTFrameRef                  _textFrame;
    CGFloat                     _fontAscent;
    CGFloat                     _fontDescent;
    CGFloat                     _fontHeight;
}

@property (nonatomic,strong)    NSMutableAttributedString *attributedString;
@property (nonatomic,strong)    M80AttributedLabelCustomLink *touchedLink;
//@property (nonatomic,assign)    BOOL linkDetected;
@property (nonatomic,assign)    BOOL ignoreRedraw;

@end

@implementation M80AttributedLabel

- (id)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        
        [self commonInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    
    self = [super initWithCoder:aDecoder];
    if (self) {
        
        [self commonInit];
    }
    return self;
}

- (void)dealloc {
    
    if (_textFrame) {
        
        CFRelease(_textFrame);
    }
    
}

#pragma mark - 初始化
- (void)commonInit {
    
    _attributedString       = [[NSMutableAttributedString alloc]init];
    _attachments                 = [[NSMutableArray alloc]init];
    _linkLocations          = [[NSMutableArray alloc]init];
    _textFrame              = nil;
    _linkColor              = [UIColor blueColor];
    _font                   = [UIFont systemFontOfSize:15];
    _textColor              = [UIColor blackColor];
    _highlightColor         = [UIColor lightGrayColor];
    _lineBreakMode          = kCTLineBreakByWordWrapping;
    //    _underLineForLink       = NO;
    //    _autoDetectLinks        = NO;
    _lineSpacing            = 0.0;
    _paragraphSpacing       = 0.0;
    if (self.backgroundColor == nil) {
        
        self.backgroundColor = [UIColor whiteColor];
    }
    
    self.type = M80AttachmentTypeNone | M80AttachmentTypeNormal |M80AttachmentTypeUserName|M80AttachmentTypeURL|M80AttachmentTypeEmotion|M80AttachmentTypePhone;
    
    self.userInteractionEnabled = YES;
    //    _isSelectedM80Label = NO;
    [self resetFont];
    
    UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
    [self addGestureRecognizer:longPress];
}

- (void)cleanAll {
    
    _ignoreRedraw = NO;
    //    _linkDetected = NO;
    [_attachments removeAllObjects];
    [_linkLocations removeAllObjects];
    //清空所有的attribute 属性
    _attributedString = [[NSMutableAttributedString alloc]init];
    self.touchedLink = nil;
    for (UIView *subView in self.subviews) {
        
        [subView removeFromSuperview];
    }
    [self resetTextFrame];
}


- (void)resetTextFrame {
    
    if (_textFrame) {
        CFRelease(_textFrame);
        _textFrame = nil;
    }
    if ([NSThread isMainThread] && !_ignoreRedraw) {
        [self setNeedsDisplay];
    }
}

- (void)resetFont {
    
    CTFontRef fontRef = CTFontCreateWithName((CFStringRef)self.font.fontName, self.font.pointSize, NULL);
    if (fontRef) {
        
        _fontAscent     = CTFontGetAscent(fontRef);
        _fontDescent    = CTFontGetDescent(fontRef);
        _fontHeight     = CTFontGetSize(fontRef);
        CFRelease(fontRef);
    }
}

#pragma mark - 属性设置
//保证正常绘制，如果传入nil就直接不处理
- (void)setFont:(UIFont *)font {
    
    if (font && _font != font) {
        _font = font;
        
        [_attributedString setFont:_font];
        [self resetFont];
        /* 主要是设置 Emotion的时候 需要及时 图片的size **/
        for (M80AttributedLabelAttachment *attachment in _attachments) {
            attachment.fontAscent = _fontAscent;
            attachment.fontDescent = _fontDescent;
        }
        [self resetTextFrame];
    }
}

- (void)setTextColor:(UIColor *)textColor {
    
    if (textColor && _textColor != textColor) {
        _textColor = textColor;
        [_attributedString setTextColor:textColor];
        
        [self resetTextFrame];
    }
}


- (void)setHighlightColor:(UIColor *)highlightColor {
    
    if (highlightColor && _highlightColor != highlightColor) {
        _highlightColor = highlightColor;
        
        [self resetTextFrame];
    }
}

- (void)setLinkColor:(UIColor *)linkColor {
    
    if (_linkColor != linkColor) {
        _linkColor = linkColor;
        
        [self resetTextFrame];
    }
}

- (void)setFrame:(CGRect)frame {
    
    CGRect oldRect = self.bounds;
    [super setFrame:frame];
    
    if (!CGRectEqualToRect(self.bounds, oldRect)) {
        
        [self resetTextFrame];
    }
}

- (void)setBounds:(CGRect)bounds {
    
    CGRect oldRect = self.bounds;
    [super setBounds:bounds];
    
    if (!CGRectEqualToRect(self.bounds, oldRect)) {
        
        [self resetTextFrame];
    }
}


#pragma mark - 辅助方法
- (NSAttributedString *)attributedString:(NSString *)text {
    
    if ([text length]) {
        
        NSMutableAttributedString *string = [[NSMutableAttributedString alloc]initWithString:text];
        [string setFont:self.font];
        [string setTextColor:self.textColor];
        return string;
    } else {
        
        return [[NSAttributedString alloc]init];
    }
}

- (NSInteger)numberOfDisplayedLines
{
    CFArrayRef lines = CTFrameGetLines(_textFrame);
    return _numberOfLines > 0 ? MIN(CFArrayGetCount(lines), _numberOfLines) : CFArrayGetCount(lines);
}

/* 获得所要 绘制的 属性字符串 **/
- (NSAttributedString *)attributedStringForDraw {
    
    if (_attributedString) {
        //添加排版格式
        NSMutableAttributedString *drawString = [_attributedString mutableCopy];
        
        //如果LineBreakMode为TranncateTail,那么默认排版模式改成kCTLineBreakByCharWrapping,使得尽可能地显示所有文字
        CTLineBreakMode lineBreakMode = self.lineBreakMode;
        if (self.lineBreakMode == kCTLineBreakByTruncatingTail) {
            
            lineBreakMode = _numberOfLines == 1 ? kCTLineBreakByCharWrapping : kCTLineBreakByWordWrapping;
        }
        /* 绘制的时候 需要用到的 字体的高度 **/
        CGFloat fontLineHeight = self.font.lineHeight;  //使用全局fontHeight作为最小lineHeight
        /* 绘制的段落 设置 **/
        CTParagraphStyleSetting settings[] = {
            /*1. 对齐方式 **/
            {kCTParagraphStyleSpecifierAlignment,sizeof(_textAlignment),&_textAlignment},
            /*2. 换行方式 **/
            {kCTParagraphStyleSpecifierLineBreakMode,sizeof(lineBreakMode),&lineBreakMode},
            /*3. 设置最大或者最小的 行间距 **/
            {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(_lineSpacing),&_lineSpacing},
            {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(_lineSpacing),&_lineSpacing},
            /*4. 段落的间距  **/
            {kCTParagraphStyleSpecifierParagraphSpacing,sizeof(_paragraphSpacing),&_paragraphSpacing},
            /*5. 行高 **/
            {kCTParagraphStyleSpecifierMinimumLineHeight,sizeof(fontLineHeight),&fontLineHeight},
        };
        CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings,sizeof(settings) / sizeof(settings[0]));
        /* 把排版格式 放入到 属性字符串中 **/
        [drawString addAttribute:(id)kCTParagraphStyleAttributeName
                           value:(__bridge id)paragraphStyle
                           range:NSMakeRange(0, [drawString length])];
        CFRelease(paragraphStyle);
        
        /* 把所有的 点击link的属性都 放入到 属性字符串中 **/
        for (M80AttributedLabelCustomLink *link in _linkLocations) {
            
            if (link.range.location + link.range.length >[_attributedString length]) {
                continue;
            }
            UIColor *drawLinkColor = link.color ? : self.linkColor;
            [drawString setTextColor:drawLinkColor range:link.range];
            [drawString setUnderlineStyle:kCTUnderlineStyleNone
                                 modifier:kCTUnderlinePatternSolid
                                    range:link.range];
        }
        return drawString;
    } else {
        
        return nil;
    }
}

// 根据某个点找到 点击的事件
- (M80AttributedLabelCustomLink *)urlForPoint:(CGPoint)point {
    
    static const CGFloat kVMargin = 5;
    if (!CGRectContainsPoint(CGRectInset(self.bounds, 0, -kVMargin), point)
        || _textFrame == nil) {
        return nil;
    }
    
    CFArrayRef lines = CTFrameGetLines(_textFrame);
    if (!lines) return nil;
    
    /* 获得CTLine 的行数 **/
    CFIndex count = CFArrayGetCount(lines);
    
    CGPoint origins[count];
    
    /*获得每一个CTline的初始点**/
    CTFrameGetLineOrigins(_textFrame, CFRangeMake(0,0), origins);
    
    CGAffineTransform transform = [self transformForCoreText];
    CGFloat verticalOffset = 0; //不像Nimbus一样设置文字的对齐方式，都统一是TOP,那么offset就为0
    
    for (int i = 0; i < count; i++) {
        
        /*获得每一个CTline的初始点**/
        CGPoint linePoint = origins[i];
        /* 获得CTline **/
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        /* 获得当前CTLine的 bounds ，主要是为了 点击的时候 变色效果 **/
        CGRect flippedRect = [self getLineBounds:line point:linePoint];
        /* 变成正确的坐标 coreText的坐标 是倒置的 **/
        CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);
        
        /* 把区域变大些 **/
        rect = CGRectInset(rect, 0, -kVMargin);
        rect = CGRectOffset(rect, 0, verticalOffset);
        
        /* 看 point是否在我们的rect 中 **/
        if (CGRectContainsPoint(rect, point)) {
            
            /* 相对于 CTLine的点 **/
            CGPoint relativePoint = CGPointMake(point.x-CGRectGetMinX(rect),
                                                point.y-CGRectGetMinY(rect));
            /* 该点 在 CTLine 是第几个中的位置，其实是 location **/
            CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
            /* 判断 这个location 是否在 _linkLocations 数组 中 **/
            M80AttributedLabelCustomLink *url = [self linkAtIndex:idx];
            if (url) {
                return url;
            }
        }
    }
    return nil;
}

// 根据某点找到 link Data
- (M80AttachmentModel *)linkDataForPoint:(CGPoint)point {
    
    M80AttributedLabelCustomLink *link = [self urlForPoint:point];
    return link ? link.linkData : nil;
}

// 坐标变换
- (CGAffineTransform)transformForCoreText {
    
    return CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f);
}

/* 获得当前CTLine的 bounds ，主要是为了 点击的时候 变色效果 **/
- (CGRect)getLineBounds:(CTLineRef)line point:(CGPoint) point {
    
    CGFloat ascent = 0.0f;
    CGFloat descent = 0.0f;
    CGFloat leading = 0.0f;
    CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
    CGFloat height = ascent + descent;
    
    return CGRectMake(point.x, point.y - descent, width, height);
}

/* 判断 这个location 是否在 _linkLocations 数组 中 **/
- (M80AttributedLabelCustomLink *)linkAtIndex:(CFIndex)index {
    
    for (M80AttributedLabelCustomLink *link in _linkLocations) {
        
        if (NSLocationInRange(index, link.range)) {
            return link;
        }
    }
    return nil;
}

/* 计算出 交集的 rect
 * 找到某一行 在 linkRange的 CGRect
 **/

- (CGRect)rectForRange:(NSRange)range
                inLine:(CTLineRef)line
            lineOrigin:(CGPoint)lineOrigin {
    
    CGRect rectForRange = CGRectZero;
    CFArrayRef runs = CTLineGetGlyphRuns(line);
    CFIndex runCount = CFArrayGetCount(runs);
    
    // Iterate through each of the "runs" (i.e. a chunk of text) and find the runs that
    // intersect with the range.
    for (CFIndex k = 0; k < runCount; k++)
    {
        CTRunRef run = CFArrayGetValueAtIndex(runs, k);
        
        /* 获得 某个 run 的字符串的范围 **/
        CFRange stringRunRange = CTRunGetStringRange(run);
        NSRange lineRunRange = NSMakeRange(stringRunRange.location, stringRunRange.length);
        
        /*当前的这个run 的字符串是否 在 linkRange 里面 **/
        NSRange intersectedRunRange = NSIntersectionRange(lineRunRange, range);
        
        if (intersectedRunRange.length == 0)
        {
            // This run doesn't intersect the range, so skip it.
            continue;
        }
        
        CGFloat ascent = 0.0f;
        CGFloat descent = 0.0f;
        CGFloat leading = 0.0f;
        
        /*
         * 计算CTRun 的宽度
         **/
        //Typographic 排版的
        // Use of 'leading' doesn't properly highlight Japanese-character link.
        CGFloat width = (CGFloat)CTRunGetTypographicBounds(run,
                                                           CFRangeMake(0, 0),
                                                           &ascent,
                                                           &descent,
                                                           NULL); //&leading);
        CGFloat height = ascent + descent;
        ////获取整段文字中charIndex位置的字符相对line的原点的x值
        /*
         * 当前的Run 在line 中的 x 值
         **/
        CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil);
        
        CGRect linkRect = CGRectMake(lineOrigin.x + xOffset - leading, lineOrigin.y - descent, width + leading, height);
        
        linkRect.origin.y = roundf(linkRect.origin.y);
        linkRect.origin.x = roundf(linkRect.origin.x);
        linkRect.size.width = roundf(linkRect.size.width);
        linkRect.size.height = roundf(linkRect.size.height);
        
        /*
         *  CGRectUnion: 返回值是能够包含两个矩形的最小矩形,意思就是把两个矩形合并返回
         **/
        rectForRange = CGRectIsEmpty(rectForRange) ? linkRect : CGRectUnion(rectForRange, linkRect);
    }
    
    return rectForRange;
}

// 要添加 一个attachment 需要 用一个 空字符串 占位符
- (void)appendAttachment: (M80AttributedLabelAttachment *)attachment
{
    attachment.fontAscent                   = _fontAscent;
    attachment.fontDescent                  = _fontDescent;
    unichar objectReplacementChar           = 0xFFFC;//一个空白的占位符
    NSString *objectReplacementString       = [NSString stringWithCharacters:&objectReplacementChar length:1];
    NSMutableAttributedString *attachText   = [[NSMutableAttributedString alloc]initWithString:objectReplacementString];
    
    CTRunDelegateCallbacks callbacks;
    callbacks.version       = kCTRunDelegateVersion1;
    callbacks.getAscent     = ascentCallback;
    callbacks.getDescent    = descentCallback;
    callbacks.getWidth      = widthCallback;
    callbacks.dealloc       = deallocCallback;
    
    CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (void *)attachment);
    NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)delegate,kCTRunDelegateAttributeName, nil];
    [attachText setAttributes:attr range:NSMakeRange(0, 1)];
    CFRelease(delegate);
    
    [_attachments addObject:attachment];
    [self appendAttributedText:attachText];
}


#pragma mark - 设置文本
- (void)setText:(NSString *)text
{
    NSAttributedString *attributedText = [self attributedString:text];
    [self setAttributedText:attributedText];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
    
    [self cleanAll]; //重新赋值
    [_attributedString appendAttributedString:attributedText];
    
}

#pragma mark - 添加文本
- (void)appendText:(NSString *)text
{
    NSAttributedString *attributedText = [self attributedString:text];
    [self appendAttributedText:attributedText];
}

- (void)appendAttributedText: (NSAttributedString *)attributedText
{
    [_attributedString appendAttributedString:attributedText];
    //#warning 测试开发 注释下面的代码
    [self resetTextFrame];
}


#pragma mark - 添加图片
- (void)appendImage: (UIImage *)image
{
    [self appendImage:image
              maxSize:image.size];
}

- (void)appendImage: (UIImage *)image
            maxSize: (CGSize)maxSize
{
    [self appendImage:image
              maxSize:maxSize
               margin:UIEdgeInsetsZero];
}

- (void)appendImage: (UIImage *)image
            maxSize: (CGSize)maxSize
             margin: (UIEdgeInsets)margin
{
    [self appendImage:image
              maxSize:maxSize
               margin:margin
            alignment:M80ImageAlignmentBottom];
}

- (void)appendImage: (UIImage *)image
            maxSize: (CGSize)maxSize
             margin: (UIEdgeInsets)margin
          alignment: (M80ImageAlignment)alignment
{
    M80AttributedLabelAttachment *attachment = [M80AttributedLabelAttachment attachmentWith:image
                                                                                     margin:margin
                                                                                  alignment:alignment
                                                                                    maxSize:maxSize];
    [self appendAttachment:attachment];
}

#pragma mark - 添加UI控件
- (void)appendView: (UIView *)view
{
    [self appendView:view
              margin:UIEdgeInsetsZero];
}

- (void)appendView: (UIView *)view
            margin: (UIEdgeInsets)margin
{
    [self appendView:view
              margin:margin
           alignment:M80ImageAlignmentBottom];
}


- (void)appendView: (UIView *)view
            margin: (UIEdgeInsets)margin
         alignment: (M80ImageAlignment)alignment
{
    M80AttributedLabelAttachment *attachment = [M80AttributedLabelAttachment attachmentWith:view
                                                                                     margin:margin
                                                                                  alignment:alignment
                                                                                    maxSize:CGSizeZero];
    [self appendAttachment:attachment];
}

#pragma mark - 添加链接
- (void)addCustomLink: (M80AttachmentModel *)linkData
             forRange: (NSRange)range {
    
    [self addCustomLink:linkData
               forRange:range
              linkColor:self.linkColor];
    
}

// 增加自定义的 点击链接
- (void)addCustomLink: (M80AttachmentModel *)linkData
             forRange: (NSRange)range
            linkColor: (UIColor *)color {
    
    M80AttributedLabelCustomLink *link = [M80AttributedLabelCustomLink customLinkWithLinkData:linkData
                                                                                        range:range
                                                                                        color:color];
    [_linkLocations addObject:link];
    [self resetTextFrame];
}

#pragma mark - 计算大小
- (CGSize)sizeThatFits:(CGSize)size
{
    NSAttributedString *drawString = [self attributedStringForDraw];
    if (drawString == nil)
    {
        return CGSizeZero;
    }
    CFAttributedStringRef attributedStringRef = (__bridge CFAttributedStringRef)drawString;
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedStringRef);
    CFRange range = CFRangeMake(0, 0);
    if (_numberOfLines > 0 && framesetter)
    {
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
        CFArrayRef lines = CTFrameGetLines(frame);
        
        if (nil != lines && CFArrayGetCount(lines) > 0)
        {
            NSInteger lastVisibleLineIndex = MIN(_numberOfLines, CFArrayGetCount(lines)) - 1;
            CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex);
            
            CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine);
            range = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length);
        }
        CFRelease(frame);
        CFRelease(path);
    }
    
    CFRange fitCFRange = CFRangeMake(0, 0);
    CGSize newSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, range, NULL, size, &fitCFRange);
    if (framesetter)
    {
        CFRelease(framesetter);
    }
    
    //hack:
    //1.需要加上额外的一部分size,有些情况下计算出来的像素点并不是那么精准
    //2.ios7的CTFramesetterSuggestFrameSizeWithConstraints方法比较残,需要多加一部分height
    //3.ios7多行中如果首行带有很多空格，会导致返回的suggestionWidth远小于真是width,那么多行情况下就是用传入的width
    if (M80IOS7)
    {
        if (newSize.height < _fontHeight * 2)   //单行
        {
            return CGSizeMake(ceilf(newSize.width) + 2.0, ceilf(newSize.height) + 4.0);
        }
        else
        {
            return CGSizeMake(size.width, ceilf(newSize.height) + 4.0);
        }
    }
    else
    {
        return CGSizeMake(ceilf(newSize.width) + 2.0, ceilf(newSize.height) + 2.0);
    }
}


- (CGSize)intrinsicContentSize {
    
    return [self sizeThatFits:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)];
}

#pragma mark -
//+ (void)setCustomDetectMethod:(M80CustomDetectLinkBlock)block
//{
//    [M80AttributedLabelURL setCustomDetectMethod:block];
//}

#pragma mark - 绘制方法
- (void)drawRect:(CGRect)rect {
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    if (ctx == nil) return;
    /*存储当前的ctx 状态 **/
    CGContextSaveGState(ctx);
    /* 变化成 正常的 坐标 **/
    CGAffineTransform transform = [self transformForCoreText];
    CGContextConcatCTM(ctx, transform);
    
    //[self recomputeLinksIfNeeded];
    //选中全部的视图
    //    [self drawAllHighlightWithRect:rect];
    
    /* 获得所要 绘制的 属性字符串 **/
    NSAttributedString *drawString = [self attributedStringForDraw];
    if (drawString) {
        //根据 属性字符串 =》 CTFramesetterRef = 》CTFrameRef
        [self prepareTextFrame:drawString rect:rect];
        //设置点击的高亮
        [self drawHighlightWithRect:rect];
        //画图片
        [self drawAttachments];
        //画文本
        [self drawText:drawString
                  rect:rect
               context:ctx];
    }
    CGContextRestoreGState(ctx);
    
}

/*
 * 获得 CTFrameRef
 **/
- (void)prepareTextFrame: (NSAttributedString *)string
                    rect: (CGRect)rect {
    
    if (_textFrame == nil) {
        
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, nil,rect);
        _textFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
        CGPathRelease(path);
        CFRelease(framesetter);
    }
}

/**
 *  画某个区域的高亮
 *
 *  @param highlightRect
 *  @param ctx
 */
- (void)drawHighlightWithHighlightRect:(CGRect)highlightRect withContextRef:(CGContextRef)ctx{
    
    if (!CGRectIsEmpty(highlightRect)){
        
        CGFloat pi = (CGFloat)M_PI;
        CGFloat radius = 1.0f;
        CGContextMoveToPoint(ctx, highlightRect.origin.x, highlightRect.origin.y + radius);
        CGContextAddLineToPoint(ctx, highlightRect.origin.x, highlightRect.origin.y + highlightRect.size.height - radius);
        CGContextAddArc(ctx, highlightRect.origin.x + radius, highlightRect.origin.y + highlightRect.size.height - radius,
                        radius, pi, pi / 2.0f, 1.0f);
        CGContextAddLineToPoint(ctx, highlightRect.origin.x + highlightRect.size.width - radius,
                                highlightRect.origin.y + highlightRect.size.height);
        CGContextAddArc(ctx, highlightRect.origin.x + highlightRect.size.width - radius,
                        highlightRect.origin.y + highlightRect.size.height - radius, radius, pi / 2, 0.0f, 1.0f);
        CGContextAddLineToPoint(ctx, highlightRect.origin.x + highlightRect.size.width, highlightRect.origin.y + radius);
        CGContextAddArc(ctx, highlightRect.origin.x + highlightRect.size.width - radius, highlightRect.origin.y + radius,
                        radius, 0.0f, -pi / 2.0f, 1.0f);
        CGContextAddLineToPoint(ctx, highlightRect.origin.x + radius, highlightRect.origin.y);
        CGContextAddArc(ctx, highlightRect.origin.x + radius, highlightRect.origin.y + radius, radius,
                        -pi / 2, pi, 1);
        CGContextFillPath(ctx);
    }
    
}

// 选中所有的视图
//- (void)drawAllHighlightWithRect:(CGRect)rect {
//
//    if (self.highlightColor && self.isSelectedM80Label) {
//        CGContextRef ctx = UIGraphicsGetCurrentContext();
//        [self.highlightColor setFill];
//        [self drawHighlightWithHighlightRect:rect withContextRef:ctx];
//        _isSelectedM80Label = NO;
//    }
//
//}

// 设置点击的高亮
- (void)drawHighlightWithRect: (CGRect)rect {
    
    /* 如果 存在 self.touchedLink 说明已经获得了 一个 link **/
    if (self.touchedLink && self.highlightColor) {
        
        /* 高亮的背景 颜色填充 **/
        [self.highlightColor setFill];
        /* 高亮的背景 的范围 **/
        NSRange linkRange = self.touchedLink.range;
        
        /* 获得所有的 CTline **/
        CFArrayRef lines = CTFrameGetLines(_textFrame);
        /* 获得 行的个数 **/
        CFIndex count = CFArrayGetCount(lines);
        /* 每行的 初始的 origin **/
        CGPoint lineOrigins[count];
        CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, 0), lineOrigins);
        /* 绘制的行的个数 因为要考虑到 当外界需要固定某个 高度的时候 就不需要真实的行的个数 **/
        NSInteger numberOfLines = [self numberOfDisplayedLines];
        /* 获得上下文 **/
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        
        for (CFIndex i = 0; i < numberOfLines; i++) {
            
            /* 获得某一行 **/
            CTLineRef line = CFArrayGetValueAtIndex(lines, i);
            /* 获得 某一行 的 字符的 range **/
            CFRange stringRange = CTLineGetStringRange(line);
            NSRange lineRange = NSMakeRange(stringRange.location, stringRange.length);
            /* 判断某一行的字符是否在我们的那个linkRange里面， 是否存在交集 **/
            NSRange intersectedRange = NSIntersectionRange(lineRange, linkRange);
            if (intersectedRange.length == 0) { //不存在交集continue
                continue;
            }
            /* 计算出 交集的 rect **/
            CGRect highlightRect = [self rectForRange:linkRange
                                               inLine:line
                                           lineOrigin:lineOrigins[i]];
            
            highlightRect = CGRectOffset(highlightRect, 0, -rect.origin.y);
            
            /* 绘制当前行的高亮 **/
            [self drawHighlightWithHighlightRect:highlightRect withContextRef:ctx];
        }
        
    }
}

/* 开始绘制到画布中  **/
- (void)drawText: (NSAttributedString *)attributedString
            rect: (CGRect)rect
         context: (CGContextRef)context {
    
    if (_textFrame) {
        
        /*如果是自定义的高度 需要根据行数来 绘制 **/
        if (_numberOfLines > 0) {
            
            CFArrayRef lines = CTFrameGetLines(_textFrame);
            NSInteger numberOfLines = [self numberOfDisplayedLines];
            
            /* 获得CTLine的起始 **/
            CGPoint lineOrigins[numberOfLines];
            CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, numberOfLines), lineOrigins);
            
            for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
                
                CGPoint lineOrigin = lineOrigins[lineIndex];
                /* 设置起始点 **/
                CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
                /* 第几个 CTLine **/
                CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
                
                //                /* 测试 **/
                //                CFRange lineRange = CTLineGetStringRange(line);
                //                NSAttributedString *truncationString = [attributedString attributedSubstringFromRange:NSMakeRange(lineRange.location, lineRange.length)];
                //                NSLog(@"line = %ld, content =  %@,lineRange = %@ ,origin = %@",lineIndex,truncationString.string,NSStringFromRange(NSMakeRange(lineRange.location, lineRange.length)),NSStringFromCGPoint(lineOrigin));
                
                
                BOOL shouldDrawLine = YES;
                
                /*  最后 一行 要 判断 是否 是kCTLineBreakByTruncatingTail **/
                if (lineIndex == numberOfLines - 1 &&
                    _lineBreakMode == kCTLineBreakByTruncatingTail) {
                    
                    // Does the last line need truncation?
                    /* 获得最后 一行 的 range **/
                    CFRange lastLineRange = CTLineGetStringRange(line);
                    
                    /* 保证 的 range 是正确的 **/
                    if (lastLineRange.location + lastLineRange.length < attributedString.length) {
                        
                        /* 省略号 的 起始位置 **/
                        NSUInteger truncationAttributePosition = lastLineRange.location + lastLineRange.length - 1;
                        /* 获取到 第truncationAttributePosition 位置的 一些属性 例如font ，颜色 ，段落 等等 **/
                        NSDictionary *tokenAttributes = [attributedString attributesAtIndex:truncationAttributePosition effectiveRange:NULL];
                        /* 获得 省略号的字符串   **/
                        NSAttributedString *tokenString = [[NSAttributedString alloc] initWithString:kEllipsesCharacter attributes:tokenAttributes];
                        /* 创建CTLine
                         * // truncation token is a CTLineRef itself
                         **/
                        CTLineRef truncationToken = CTLineCreateWithAttributedString((CFAttributedStringRef)tokenString);
                        
#if 0
                        /*
                         * 修改M80 里面的一些代码
                         **/
                        
                        /* 获得当前 行的 所有的 文字 **/
                        NSMutableAttributedString *truncationString = [[attributedString attributedSubstringFromRange:NSMakeRange(lastLineRange.location, lastLineRange.length)] mutableCopy];
                        
                        if (lastLineRange.length > 0) {
                            // Remove last token
                            [truncationString deleteCharactersInRange:NSMakeRange(lastLineRange.length - 1, 1)];
                        }
                        /* 拼接 省略号 **/
                        [truncationString appendAttributedString:tokenString];
                        
                        /* 创建**/
                        CTLineRef truncationLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationString);
#else
                        /*
                         * m80 里面设计不合理 应该是 要找到 当前行的以后的所有文字
                         *
                         **/
                        
                        /* 获得当前行的location 到整个属性字符串的最后的总长度 **/
                        NSRange truncationAllRange;
                        truncationAllRange.location = lastLineRange.location;
                        truncationAllRange.length   = [attributedString length] - truncationAllRange.location;
                        /* 获得 当前行的location 到整个属性字符串的最后的总长度 的属性字符串 **/
                        NSAttributedString *truncationString = [attributedString attributedSubstringFromRange:truncationAllRange];
                        /* 属性字符串 =》 CTLine **/
                        CTLineRef truncationLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationString);
                        
                        
#endif
                        
                        /*
                         * 通过 CTLineCreateTruncatedLine 创建最终的 带省略号的 line
                         **/
                        CTLineTruncationType truncationType = kCTLineTruncationEnd;
                        CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, rect.size.width, truncationType, truncationToken);
                        
                        if (!truncatedLine) {
                            // If the line is not as wide as the truncationToken, truncatedLine is NULL
                            truncatedLine = CFRetain(truncationToken);
                        }
                        CFRelease(truncationLine);
                        CFRelease(truncationToken);
                        
                        CTLineDraw(truncatedLine, context);
                        CFRelease(truncatedLine);
                        
                        shouldDrawLine = NO;
                    }
                }
                /* 判断是否 可以 绘制 当前的一行 **/
                if(shouldDrawLine) {
                    
                    CTLineDraw(line, context);
                }
            }
        }
        else {
            /* 直接 绘制 **/
            CTFrameDraw(_textFrame,context);
        }
    }
}

//画图片
- (void)drawAttachments {
    
    if ([_attachments count] == 0) {
        
        return;
    }
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    if (ctx == nil) {
        return;
    }
    
    CFArrayRef lines = CTFrameGetLines(_textFrame);
    CFIndex lineCount = CFArrayGetCount(lines);
    CGPoint lineOrigins[lineCount];
    CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, 0), lineOrigins);
    NSInteger numberOfLines = [self numberOfDisplayedLines];
    for (CFIndex i = 0; i < numberOfLines; i++) {
        
        /*  获得当前的 CTline**/
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        /*  获得当前的 CTRun s**/
        CFArrayRef runs = CTLineGetGlyphRuns(line);
        CFIndex runCount = CFArrayGetCount(runs);
        /*  获得当前的 CTline 的 初始点 **/
        CGPoint lineOrigin = lineOrigins[i];
        CGFloat lineAscent;
        CGFloat lineDescent;
        CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, NULL);
        CGFloat lineHeight = lineAscent + lineDescent;
        CGFloat lineBottomY = lineOrigin.y - lineDescent;
        
        // Iterate through each of the "runs" (i.e. a chunk of text) and find the runs that
        // intersect with the range.
        for (CFIndex k = 0; k < runCount; k++) {
            
            /*  获得某一个 CTRun**/
            CTRunRef run = CFArrayGetValueAtIndex(runs, k);
            
            /*如果当前的run 存在 delegate 就说明 是我们的图片 **/
            NSDictionary *runAttributes = (NSDictionary *)CTRunGetAttributes(run);
            CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAttributes valueForKey:(id)kCTRunDelegateAttributeName];
            if (nil == delegate) {
                continue;
            }
            
            /*  通过 这个代理 获得 图片 的一个对象属性M80AttributedLabelAttachment **/
            M80AttributedLabelAttachment* attributedImage = (M80AttributedLabelAttachment *)CTRunDelegateGetRefCon(delegate);
            CGFloat ascent = 0.0f;
            CGFloat descent = 0.0f;
            CGFloat width = (CGFloat)CTRunGetTypographicBounds(run,
                                                               CFRangeMake(0, 0),
                                                               &ascent,
                                                               &descent,
                                                               NULL);
            
            CGFloat imageBoxHeight = [attributedImage boxSize].height;
            
            /*  获得某一个 CTRun 在 这个CTLine的偏移量 x **/
            CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil);
            /* 根据 对其 属性 来设置 图片的 y值 **/
            CGFloat imageBoxOriginY = 0.0f;
            switch (attributedImage.alignment) {
                    
                case M80ImageAlignmentTop:
                    imageBoxOriginY = lineBottomY + (lineHeight - imageBoxHeight);
                    break;
                case M80ImageAlignmentCenter:
                    imageBoxOriginY = lineBottomY + (lineHeight - imageBoxHeight) / 2.0;
                    break;
                case M80ImageAlignmentBottom:
                    imageBoxOriginY = lineBottomY;
                    break;
            }
            /* 获得图片的 rect **/
            CGRect rect = CGRectMake(lineOrigin.x + xOffset, imageBoxOriginY, width, imageBoxHeight);
            UIEdgeInsets flippedMargins = attributedImage.margin;
            CGFloat top = flippedMargins.top;
            flippedMargins.top = flippedMargins.bottom;
            flippedMargins.bottom = top;
            
            /*图片的真实 Rect**/
            CGRect attatchmentRect = UIEdgeInsetsInsetRect(rect, flippedMargins);
            
            if (i == numberOfLines - 1 &&
                k >= runCount - 2 &&
                _lineBreakMode == kCTLineBreakByTruncatingTail) {
                
                //最后行最后的2个CTRun需要做额外判断
                CGFloat attachmentWidth = CGRectGetWidth(attatchmentRect);
                const CGFloat kMinEllipsesWidth = attachmentWidth;
                if (CGRectGetWidth(self.bounds) - CGRectGetMinX(attatchmentRect) - attachmentWidth <  kMinEllipsesWidth) {
                    continue;
                }
            }
            
            id content = attributedImage.content;
            /*在attatchmentRect区域中 绘制图片 **/
            if ([content isKindOfClass:[UIImage class]]) {
                
                CGContextDrawImage(ctx, attatchmentRect, ((UIImage *)content).CGImage);
                
            } else if ([content isKindOfClass:[UIView class]]) {
                
                UIView *view = (UIView *)content;
                if (view.superview == nil) {
                    [self addSubview:view];
                }
                CGRect viewFrame = CGRectMake(attatchmentRect.origin.x,
                                              self.bounds.size.height - attatchmentRect.origin.y - attatchmentRect.size.height,
                                              attatchmentRect.size.width,
                                              attatchmentRect.size.height);
                [view setFrame:viewFrame];
            } else {
                
                NSLog(@"Attachment Content Not Supported %@",content);
            }
            
        }
    }
}


#pragma mark - 点击事件处理
- (BOOL)onLabelClick:(CGPoint)point {
    
    id linkData = [self linkDataForPoint:point];
    if (linkData) {
        if (_delegate && [_delegate respondsToSelector:@selector(m80AttributedLabel:clickedOnLink:)]) {
            
            [_delegate m80AttributedLabel:self clickedOnLink:linkData];
        }
        return YES;
    }
    return NO;
}


//#pragma mark - 链接处理
//- (void)recomputeLinksIfNeeded
//{
//    const NSInteger kMinHttpLinkLength = 5;
//    if (!_autoDetectLinks || _linkDetected)
//    {
//        return;
//    }
//    NSString *text = [[_attributedString string] copy];
//    NSUInteger length = [text length];
//    if (length <= kMinHttpLinkLength)
//    {
//        return;
//    }
//    BOOL sync = length <= M80MinAsyncDetectLinkLength;
//    [self computeLink:text
//                 sync:sync];
//}

//- (void)computeLink:(NSString *)text
//               sync:(BOOL)sync
//{
//    __weak typeof(self) weakSelf = self;
//    typedef void (^LinkBlock) (NSArray *);
//    LinkBlock block = ^(NSArray *links)
//    {
//        weakSelf.linkDetected = YES;
//        if ([links count])
//        {
//            for (M80AttributedLabelURL *link in links)
//            {
//                [weakSelf addAutoDetectedLink:link];
//            }
//            [weakSelf resetTextFrame];
//        }
//    };
//
//    if (sync)
//    {
//        _ignoreRedraw = YES;
//        NSArray *links = [M80AttributedLabelURL detectLinks:text];
//        block(links);
//        _ignoreRedraw = NO;
//    }
//    else
//    {
//        dispatch_sync(get_m80_attributed_label_parse_queue(), ^{
//
//            NSArray *links = [M80AttributedLabelURL detectLinks:text];
//
//            dispatch_async(dispatch_get_main_queue(), ^{
//                NSString *plainText = [[weakSelf attributedString] string];
//                if ([plainText isEqualToString:text])
//                {
//                    block(links);
//                }
//            });
//        });
//    }
//}

////增加 eg: www.baidu.com的链接
//- (void)addAutoDetectedLink: (M80AttributedLabelURL *)link
//{
//    NSRange range = link.range;
//    for (M80AttributedLabelURL *url in _linkLocations)
//    {
//        if (NSIntersectionRange(range, url.range).length != 0)
//        {
//            return;
//        }
//    }
//    [self addCustomLink:link.linkData
//               forRange:link.range];
//}

#pragma mark - 点击事件相应



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if (self.touchedLink == nil) {
        
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self];
        self.touchedLink =  [self urlForPoint:point];
        //NSLog(@"point = %@",NSStringFromCGPoint(point));
    }
    if (self.touchedLink) {
        
        /* 设置选中效果 **/
        [self setNeedsDisplay];
    } else {
        
        [super touchesBegan:touches withEvent:event];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
    [super touchesMoved:touches withEvent:event];
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    
    M80AttributedLabelCustomLink *touchedLink = [self urlForPoint:point];
    if (self.touchedLink != touchedLink) {
        
        self.touchedLink = touchedLink;
        [self setNeedsDisplay];
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    
    [super touchesCancelled:touches withEvent:event];
    if (self.touchedLink) {
        
        /*去掉选中效果 **/
        self.touchedLink = nil;
        [self setNeedsDisplay];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    
    if(![self onLabelClick:point]) {
        
        [super touchesEnded:touches withEvent:event];
    }
    /*
     * onLabelClick触发点击事件
     **/
    if (self.touchedLink) {
        
        /*去掉选中效果 **/
        self.touchedLink = nil;
        [self setNeedsDisplay];
    }
}

//- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
//{
//    M80AttributedLabelURL *touchedLink = [self urlForPoint:point];
//    if (touchedLink == nil)
//    {
//        NSArray *subViews = [self subviews];
//        for (UIView *view in subViews)
//        {
//            CGPoint hitPoint = [view convertPoint:point
//                                         fromView:self];
//
//            UIView *hitTestView = [view hitTest:hitPoint
//                                      withEvent:event];
//            if (hitTestView)
//            {
//                return hitTestView;
//            }
//        }
//        return nil;
//    }
//    else
//    {
//        return self;
//    }
//}



/**
 *  从外界传入值
 *
 *  @param m80Text
 */

- (void)setM80Text:(NSString *)m80Text {
    
    _m80Text = m80Text;
    [self cleanAll];
    if (m80Text.length >0) {
        
        NSMutableArray * parts = [[NSMutableArray array]init];
        //解析出特殊和非特殊的字符串 并放到parts 数组中
        [self parserTextWithDataArr:parts withText:m80Text];
        // 数组排序 和 range 也要重新排列数组
        [self sortAndRangeWithDataArr:parts];
        //开始设置 值
        [self setM80TextWithDataArr:parts];
        //#warning 如果涉及到性能方面的可以把 appendText 的[self resetTextFrame] 注释掉 只在下面执行这段话
        //[self resetTextFrame];
    }
}

/*
 * 根据dataArr 设置M80的值
 **/
- (void)setM80TextWithDataArr:(NSMutableArray *)dataArr {
    
    NSMutableArray *parts = dataArr;
    for (M80AttachmentModel *tempPart  in parts) {
        
        
        if (tempPart.attachmentType & M80AttachmentTypeUserName) {/* 用户名 **/
            
            [self appendText:tempPart.attachmentName];
            [self addCustomLink:tempPart
                       forRange:tempPart.range linkColor:_linkColor];
            
        }else if (tempPart.attachmentType & M80AttachmentTypeURL) {/* URL **/
            
            [self appendText:tempPart.attachmentName];
            [self addCustomLink:tempPart
                       forRange:tempPart.range linkColor:_linkColor];
        }else  if (tempPart.attachmentType & M80AttachmentTypeEmotion) {/* 表情 **/
            
            UIFont *font = self.font?self.font:[UIFont systemFontOfSize:15];
            CGFloat fontWH =  font.ascender - font.descender;
            NSString * imageName = [NSString stringWithFormat:@"TUIKitFace.bundle/emoji/%@",tempPart.attachmentName];
            [self appendImage:[UIImage imageNamed:imageName]
                      maxSize:CGSizeMake(fontWH, fontWH)
                       margin:UIEdgeInsetsZero
                    alignment:M80ImageAlignmentCenter];
            
        }else if(tempPart.attachmentType & M80AttachmentTypePhone){/*手机 **/
            [self appendText:tempPart.attachmentName];
            [self addCustomLink:tempPart
                       forRange:tempPart.range linkColor:_linkColor];
        } else {/* 其他字符 **/
            
            [self appendText:tempPart.attachmentName];
        }
    }
    
}

/**
 *  数组排序 和 range 重新排列数组
 *
 *  @param dataArr
 */
- (void)sortAndRangeWithDataArr:(NSMutableArray *)dataArr {
    
    NSMutableArray *parts = dataArr;
    // 排序
    // 系统是按照从小 -> 大的顺序排列对象
    [parts sortUsingComparator:^NSComparisonResult(M80AttachmentModel *part1, M80AttachmentModel *part2) {
        if (part1.range.location > part2.range.location) {
            return NSOrderedDescending;
        }
        return NSOrderedAscending;
    }];
    
    //重新布局新的的range
    NSInteger preLoc = 0;
    for (M80AttachmentModel *tempPart  in parts) {
        NSRange range = NSMakeRange(preLoc, tempPart.attachmentName.length);
        //        if (tempPart.linkType == WXLinkTypeEmotion) { //表情图
        //            range.length = 1;
        //        }
        tempPart.range = range;
        preLoc = range.location + range.length;
    }
    
}

/**
 *  解析出特殊和非特殊的字符串
 *
 *  @param dataArr 传过来的数组
 *  @param text    文本
 */
- (void)parserTextWithDataArr:(NSMutableArray *)dataArr withText:(NSString *)text{
    
    NSMutableArray *parts = dataArr;
    
    /*记录当前的所有的正则表达的式，为了下面求非特殊的表达式 **/
    NSMutableArray * patternArr = [[NSMutableArray alloc]init];
    //固定的中文、数字、特殊字符等的匹配
    NSString * contentP = @"[0-9a-zA-Z\\u4e00-\\u9fa5\\s\\-，－\\[\\]。°？！；……,～.·?!;；@$#&*^%%:：—~&()（）“”‘’\"'_]";
    
    //Phone
    if (self.type&M80AttachmentTypePhone) {
        
        NSString *urlPattern = @"\\d{3}-\\d{8}|\\d{4}-\\d{7}|\\d{11}";
        
        [patternArr addObject:urlPattern];
        [text enumerateStringsMatchedByRegex:urlPattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            if ((*capturedRanges).length == 0) return;
            //        NSLog(@"*capturedStrings1 = %@",*capturedStrings);
            NSString * urlStr = *capturedStrings;
            
            M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
            part.attachmentName = urlStr;
            part.attachmentType  = M80AttachmentTypePhone;
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
    }
    //URL
    if (self.type&M80AttachmentTypeURL) {
        
        NSString *urlPattern = @"((http[s]?)://)?[a-zA-Z]+\\.[\\w\\.]+\\.([a-zA-Z]{2,})(:\\d+)?([\\w\\.\\-\\*\\+\\?\\^%/&=~!#$:<>]*)?";
        urlPattern = [NSString stringWithFormat:@"%@|\\[%@\\|\\|%@*\\]",urlPattern,urlPattern,contentP];
        [patternArr addObject:urlPattern];
        [text enumerateStringsMatchedByRegex:urlPattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            if ((*capturedRanges).length == 0) return;
            //        NSLog(@"*capturedStrings1 = %@",*capturedStrings);
            NSString * urlStr = *capturedStrings;
            
            M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
            part.attachmentName = urlStr;
            part.attachmentType  = M80AttachmentTypeURL;
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
    }
    
    //表情的正则表达式[哈哈]
    if (self.type&M80AttachmentTypeEmotion) {
        
//        NSString *emotionPattern = @"\\[[0-9]{2}:[\\u4e00-\\u9fa5]{1,5}\\]";
        NSString *emotionPattern = @"\\[([a-zA-Z\\u4e00-\\u9fa5])+\\]";

        [patternArr addObject:emotionPattern];
        [text enumerateStringsMatchedByRegex:emotionPattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            if ((*capturedRanges).length == 0) return;
            //        NSLog(@"*capturedStrings1 = %@",*capturedStrings);
            M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
            
            NSString * captured = *capturedStrings;
            NSString * imageName = captured;//[WXEaseEmotionTool sharedManager].convertEmtionDict[captured];

            if (imageName.length) {
                part.attachmentName = imageName;
                part.attachmentType  = M80AttachmentTypeEmotion;
            }else {
                part.attachmentName  = [NSString stringWithFormat:@"[%@]",imageName];
                part.attachmentType  = M80AttachmentTypeNormal;
            }
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
    }
    
    
    /*
     * 用户名称正则表达式
     * eg： [[lichongyang||||123]]
     * enumerateStringsMatchedByRegex （匹配 ）
     **/
    if (self.type&M80AttachmentTypeUserName) {
        
        NSString *userNamePatternTemp = [NSString stringWithFormat:@"\\[\\[@?%@+:?\\|\\|\\|\\|[0-9]+\\]\\]",contentP];
        [patternArr addObject:userNamePatternTemp];
        [text enumerateStringsMatchedByRegex:userNamePatternTemp usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            
            if ((*capturedRanges).length == 0) return;
            
            M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
            part.attachmentType  = M80AttachmentTypeUserName;
            NSString * captured = *capturedStrings;
            /* 去掉 [ ]**/
            captured = [captured substringWithRange:(NSRange){2,captured.length-4}];
            /*分开 名字 和 id **/
            NSArray * textArr = [captured componentsSeparatedByString:@"||||"];
            part.attachmentName  = textArr.firstObject;
            part.attachmentOther = textArr.lastObject;
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
    }
    
    /*
     * 遍历所有的非特殊字符
     * enumerateStringsSeparatedByRegex （Separated 和前面的表达式区别，相反的意思）
     **/
    NSString * pattern = [patternArr componentsJoinedByString:@"|"];
    if (pattern.length == 0) {
        
        M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
        part.attachmentType  = M80AttachmentTypeNormal;
        part.attachmentName = _m80Text;
        part.range = NSMakeRange(0, _m80Text.length);
        [parts addObject:part];
        
    }else {
        
        [text enumerateStringsSeparatedByRegex:pattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
            
            if ((*capturedRanges).length == 0) return;
            
            M80AttachmentModel *part = [[M80AttachmentModel alloc] init];
            part.attachmentType  = M80AttachmentTypeNormal;
            part.attachmentName = *capturedStrings;
            part.range = *capturedRanges;
            [parts addObject:part];
        }];
    }
    
    
    
}

/**
 *  长按键的触发方法
 *
 *  @param longPress
 */
- (void)longPressAction:(UILongPressGestureRecognizer *)longPress {

    if (longPress.state == UIGestureRecognizerStateBegan) {

        if ([self.delegate respondsToSelector:@selector(m80AttributedLabel:longPressWtihText:longPressPoint:)]) {

            CGPoint point = [longPress locationInView:self];
            [self.delegate m80AttributedLabel:self longPressWtihText:self.m80Text longPressPoint:point];
        }

    }

}
/**
 *  label可以成为第一响应者
 */
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

/**
 *  主要用来是否选中
 *
 *  @param isSelectedM80Label
 */
//- (void)setIsSelectedM80Label:(BOOL)isSelectedM80Label {
//    _isSelectedM80Label = isSelectedM80Label;
//    [self setNeedsDisplay];
//}

@end
